package fr.umlv.ig.adapter;


import java.util.ArrayList;
import java.util.EventObject;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;

import fr.umlv.ig.event.StationEvent;
import fr.umlv.ig.listener.SearchListener;
import fr.umlv.ig.listener.StationAdapter;
import fr.umlv.ig.model.Line;
import fr.umlv.ig.model.MetroModelImpl;
import fr.umlv.ig.model.Station;

/**
 * Adapter to have a filter for the Station. 
 * @author jenseric
 * @author william
 *
 */
public class FilterModel  {
	
	/**
	 * Stations which are include to the filtre
	 */
	private final ArrayList<Station> stations = new ArrayList<Station>();
	/**
	 * Used when a the model send event to the listeners
	 */
	private boolean firing = false;
	
	/**
	 * List of listeners which listen the model.
	 */
	private final ArrayList<SearchListener> listeners = new ArrayList<SearchListener>();
	
	
	/**
	 * Represents the stations on the model MetromodelImpl
	 */
	private final TreeMap<String, Station> mapStations = new TreeMap<String, Station>();
	
	/**
	 * The model which the FilterModel used.
	 */
	private final MetroModelImpl model;

	/**
	 * Initializes the model.
	 * @param model the model which used in the filterModel.
	 */
	public FilterModel(MetroModelImpl model) {
		this.model = model;
		SortedMap<String, Station> sortedMap = model.getMapStation();
		for (Iterator<Entry<String,Station>> i = sortedMap.entrySet().iterator() ; i.hasNext() ; ){ 
			Entry<String,Station> e = i.next();
			mapStations.put(e.getKey(), e.getValue());
			stations.add(e.getValue());
		}
		
		model.addStationListener(new StationAdapter() {

			@Override
			public void stationChange(StationEvent event) {
				Station station = (Station) event.getSource();
				if(stations.contains(station)) {
					fireFilter(new EventObject(event.getSource()));
				}
			}
		});
	}
	
	/**
	 * The number of stations filtred.
	 * @return the number of stations filtred.
	 */
	public int size() {
		return stations.size();
		
	}
	
	/**
	 * The method called when a user launch a search.
	 * @param prefix
	 */
	public void filtre(String prefix) {
		stations.clear();
		String prefixUpper = prefix.toUpperCase();
		SortedMap<String, Station> sousMap = mapStations.tailMap(prefixUpper);

		for (Iterator<String> i = sousMap.keySet().iterator() ; i.hasNext() ; ){

			String nameId = i.next();
			Station station = mapStations.get(nameId);
		    if((nameId.startsWith(prefixUpper))) {
			    stations.add(station);
		    }
		    else {
		    	
	fireFilter(new EventObject(this));
		    return;
		    }
		}
		fireFilter(new EventObject(this));
	}
	
	/**
	 * Prevents every listeners connected to the model that a change append.
	 * @param event
	 */
	protected void fireFilter(EventObject event) {
		try {
			firing = true;
			int index1;
			int index2;
			if(event.getSource() == this) {
				index1=0;
				index2= stations.size();
			}
			else {
				Station station = (Station) event.getSource();
				index1 = stations.indexOf(station);
				index2 = index1;
			}
			for(SearchListener listener : listeners) {
				listener.changed(event, index1, index2);
			}
		} finally {
			firing = false;
		}
	}

	
	/**
	 * Adds a listeners. 
	 * @param listener the listener to add.
	 */
	public void addListener(SearchListener listener) {
		if(firing) {
			throw new IllegalStateException("Cannot remove listeners while fire... is using them");
		}		
		listeners.add(listener);
	}


	/**
	 * Removes listener.
	 * @param listener the listener to remove.
	 */
	public void removeListener(SearchListener listener) {
		if(firing) {
			throw new IllegalStateException("Cannot remove listeners while fire... is using them");
		}		
		listeners.remove(listener);
	}


	/**
	 * Opens a station.
	 * @param station the station to open.
	 */
	public void openStation(Station station) {
		model.openStation(station);
		
	}

	/**
	 * Closes a station.
	 * @param station the station to close.
	 */
	public void closeStation(Station station) {
		model.closeStation(station);
	}

	/**
	 * Gets the line of the station at the index in parameter.
	 * @param index the index of the station.
	 * @return the line of the station at the index in parameter.
	 */
	public Line getLine(int index) {
		return model.getLine(stations.get(index));
	}

	/**
	 * Returns the Station at the index in parameter.
	 * @param index the index where the station is.
	 * @return the Station at the index in parameter.
	 */
	public Station getStation(int index) {
		return stations.get(index);
	}

	/**
	 * Returns true if the station at tthe index in parameter is opened, else 
	 * false.
	 * @param index the index of the station. 
	 * @return true if the station at the index in parameter is opened, else 
	 * false.
	 */
	public Boolean getBoolean(int index) {
		return model.isOpened(stations.get(index));
	}


	/**
	 * Selects the station in parameter.
	 * @param station the station to select
	 */
	public void selected(Station station) {
		model.selected(station);
	}

}
